// 测试 Object 静态属性

/** - - - Object.create( instance | null ) - - - */

const proto = { type: '动物' }

// 以参数指定的实例为原型创建一个新的实例
let instance = Object.create( proto );
console.log( instance );

console.log( instance.type );
console.log( Object.getOwnPropertyNames( instance ) );
console.log( instance.__proto__ === proto ); // true
console.log( Object.getPrototypeOf( instance ) === proto ); // true

// 创建一个纯对象(即没有继承任何原型的实例)
let pure = Object.create(null);
console.log( Object.getOwnPropertyNames( pure ) );
console.log( pure.__proto__ ); // undefined
console.log( Object.getPrototypeOf( pure ) ); // null
// 尝试调用从 Object 继承的方法，结果抛出 TypeError
// console.log( pure.toString() ); // TypeError: pure.toString is not a function

console.log( '- '.repeat( 20 ) );

/** - - - Object.assign(target, ...sources) - - - */

let target = Object.create( null );
console.log( target );

let student = { id : 1001 , name : '小姜' }
let teacher = { id : 2002 , nickname: '相爷' }

Object.assign( target , student , teacher );

console.log( target );

console.log( '- '.repeat( 20 ) );

/** - - - Object.keys(target) / Object.values(target) / Object.entries(target) - - - */

// 使用对象字面量创建对象(实例级别)
let panda = {
    id: 1 ,
    name: '熊猫儿',
    age: 5,
    introduce(){
        console.log( `我是${this.name}` );
    } 
}

// 为 panda 实例 定义 gender 属性，除显式设置 value 外，其它描述都采用默认值:
// enumerable: false , configurable: false , writable: false
Object.defineProperty( panda , 'gender' , { value: '雄' } );

// 获取指定实例中所有可枚举属性【名】组成的数组
console.log( Object.keys( panda ) ); 

// 获取指定实例中所有可枚举属性【值】组成的数组
console.log( Object.values( panda ) );

// 获取指定实例中所有可枚举属性组成的数组
const entries = Object.entries( panda );
for( let i = 0 , n = entries.length ; i < n ; i++ ){
    //数组中的每个元素都是一个小数组
    let entry = entries[ i ] ;
    console.log( entry[ 0 ] , entry[ 1 ] );
}

/** - - - 定义属性/获取属性描述信息 - - - */

const p = Object.create( null );

// 为指定实例定义指定名称的属性 (单个属性)
Object.defineProperty( p , 'id' , { 
        value: 1001 , 
        enumerable: true 
    } );

console.log( p );

// 从指定实例中获得指定属性的描述信息 (单个属性)
const idDescriptor = Object.getOwnPropertyDescriptor( p , 'id' );
console.log( idDescriptor );

// 为指定实例定义一批属性 (多个属性)
Object.defineProperties( p , {
    'name' : { value: '小黄' , enumerable: true , writable: true , configurable: false } ,
    'gender' : { value: '靓仔' , enumerable: true }
});

console.log( p );

// 获得指定实例的所有属性的描述信息 (多个属性)
const descriptors = Object.getOwnPropertyDescriptors( p );
console.log( descriptors );


/** - - - 封闭对象(实例级别的对象) - - - */

const loong = Object.create(null);

Object.defineProperties( loong , {
    'id' : { value: 1 , enumerable: true , writable: true , configurable: true } ,
    'name' : { value: '敖广' , enumerable: true , writable: true , configurable: true } ,
    'gender' : { value: '靓仔' , enumerable: true , writable: false , configurable: true } 
});

console.log( loong );
// 判断指定实例是否是已封闭的
console.log( 'isSealed: ' , Object.isSealed( loong ) ); // false 
console.log( 'isExtensible: ' , Object.isExtensible( loong ) ); // true
// 封闭指定实例:
Object.seal( loong ) ;
console.log( 'isSealed: ' , Object.isSealed( loong ) ); // true 
// 判断指定实例是否可以被扩展(是否可以在它上面添加新的属性)
console.log( 'isExtensible: ' , Object.isExtensible( loong ) ); // false

// 封闭一个对象，阻止添加新属性
loong.type = '动物';
console.log( loong );


// 封闭一个对象，将所有现有属性标记为不可配置
delete loong.name ;
console.log( loong );
// 也可以获得所有属性的描述信息来查看 configurable 取值
// console.log( Object.getOwnPropertyDescriptors( loong ) );

// 封闭一个对象，当前属性的值只要原来是可写的就可以改变
loong.id = 2 ;
loong.name = '敖钦' ;
loong.gender = '靓妹';

console.log( loong );

/** - - - 冻结对象(实例级别的对象) - - - */

const tiger = {
    id: 9527 , 
    name: '泰哥',
    gender: '靓仔'
}

console.log( '是否可扩展' , Object.isExtensible( tiger ) );
console.log( '是否已封闭' , Object.isSealed( tiger ) );
console.log( '是否已冻结' , Object.isFrozen( tiger ) );

// Object.freeze() 方法可以冻结一个对象
const another = Object.freeze( tiger );

// 一个被冻结的对象再也不能被修改；
// 冻结了一个对象则不能向这个对象添加新的属性，不能删除已有属性，
tiger.age = 10 ;
// Object.defineProperty( tiger , 'type' , { value: '东北虎' } ) // TypeError: Cannot define property type, object is not extensible
console.log( tiger );
// 不能修改该对象已有属性的可枚举性、可配置性、可写性，
const nameDescriptor = Object.getOwnPropertyDescriptor( tiger , 'name' );
console.log( nameDescriptor );
nameDescriptor.configurable = true ;

console.log( Object.getOwnPropertyDescriptor( tiger , 'name' ) );

delete tiger.name ;
console.log( tiger );

// 以及不能修改已有属性的值。
tiger.id = 250;
console.log( tiger );

// 此外，冻结一个对象后该对象的原型也不能被修改。
// freeze() 返回和传入的参数相同的对象。
console.log( another === tiger ); // true

/** - - - Object.fromEntries() - - - */

const studentEntries = [
    ['id' , 1001 ],
    ['name' , '张三丰' ],
    ['gender' , '男' ],
    ['age' , 108 ]
];

const stu = Object.fromEntries( studentEntries );
console.log( stu );

/** - - - Object.is( first , second ) - - - */

let first = '12345';
let second = 12345 ;
console.log( first == second ); // true
console.log( first === second ); // false

console.log( Object.is( first , second ) ); // false

second = '12345' ;
console.log( Object.is( first , second ) ); // true

first = { id: 1001 , name: '张三丰' };
second = new Object();
second.id = 1001 ;
Object.defineProperty( second , 'name' , { value: '张三丰' } )

console.log( first == second ); // false
console.log( Object.is( first, second ) ); // false

first = { id: 2002 , name: '张翠山' };
second = { id: 2002 , name: '张翠山' };

console.log( first == second ); // false
console.log( Object.is( first, second ) ); // false

second = first ;
console.log( Object.is( first, second ) ); // true
